﻿using StarryEdge.Core;
using StarryEdge.Core.Device;
using StarryEdge.Core.Enums;
using StarryEdge.Core.Models;
using StarryEdge.Devices.Tool;
using System;
using System.Collections.Generic;
using System.Diagnostics.Metrics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace StarryEdge.Devices.MeterDevice.Modbus
{
    public class Modbus : PLCBase, IPLC<int>
    {
        private byte _slave_id;
        public Modbus(byte slave_id, ICommunication com, IStarryEdgeLog edgeLog, EndianTypes endianType, IProtocol protocol, Func<DeviceInfoModel, IDevice, bool> func): 
            base(com, edgeLog, endianType, func)
        {
            _slave_id = slave_id;
            _protocol = protocol;
        }

        public bool Open() => _com.Open();

        public void Close() => _com.Close();

        public IEnumerable<TValue> Read<TValue>(int address, int length = 3)
        {
            byte[] bytes = new byte[5];
            bytes[0] = 0x03;
            bytes[1] = (byte)(address >> 8);
            bytes[2] = (byte)address;
            bytes[3] = 0x00;
            Type type = typeof(TValue);
            switch (type.Name)
            {
                case "Int32":
                case "UInt32":
                case "Single":
                    length = 2 * length;
                    break;
                case "Double":
                    length = 4 * length;
                    break;
                case "Int16":
                case "UInt16":
                    break;
                case "Int64":
                case "UInt64":
                    length = 8 * length;
                    break;
                case "Boolean":
                case "Byte":
                    throw new Exception($"没有实现 {type.Name}");
            }
            bytes[3] = (byte)(length >> 8);
            bytes[4] = (byte)(length);
            bytes = SendToRead(bytes, this);
            double count = 0;
            List<TValue> valuedata = new List<TValue>();
            while (count < bytes.Length)
            {
                valuedata.Add((TValue)StructToBytes.GetValue(type, bytes, ref count, _endianType));
            }
            return valuedata;
        }

        public IEnumerable<TValue> Read<TValue>(int[] address, int length = 3)
        {
            throw new NullReferenceException("CIP协议用");
        }

        public TClass? ReadClass<TClass>(int address, TClass obj = null, int count = 3) where TClass : class
        {
            if (obj == null) { obj = Activator.CreateInstance<TClass>(); }
            int classSize = (int)StructToBytes.GetClassSize(obj);
            if (classSize > 252)
            {
                throw new Exception("超过Modbus最大字节数");
            }
            byte[] bytes = new byte[5];
            bytes[0] = 0x03;
            bytes[1] = (byte)(address >> 8);
            bytes[2] = (byte)address;
            bytes[3] = (byte)(classSize >> 8);
            bytes[4] = (byte)classSize;
            bytes = SendToRead(bytes, this, count);
            if (bytes == null) { return default; }
            int boolSize = 0;
            StructToBytes.FromBytes(obj, bytes, ref boolSize, 0, _endianType);
            return obj;
        }

        public TValue ReadSingle<TValue>(int address, int length)
        {
            byte[] bytes = new byte[5];
            bytes[0] = 0x03;
            bytes[1] = (byte)(address >> 8);
            bytes[2] = (byte)address;
            bytes[3] = 0x00;
            Type type = typeof(TValue);
            switch (type.Name)
            {
                case "Int32":
                case "UInt32":
                case "Single":
                    bytes[4] = 2;
                    break;
                case "Double":
                    bytes[4] = 4;
                    break;
                case "Int16":
                case "UInt16":
                    bytes[4] = 1;
                    break;
                case "Boolean":
                case "Byte":
                case "Int64":
                case "UInt64":
                    throw new Exception($"没有实现 {type.Name}");
            }
            bytes = SendToRead(bytes, this);
            double kl = 0;
            return (TValue)StructToBytes.GetValue(type, bytes, ref kl, _endianType);
        }

        public bool Write(int address, IEnumerable<object> value)
        {
            List<byte> list = new List<byte>() { 0x10 };
            list.Add((byte)(address >> 8));
            list.Add((byte)address);
            List<byte> valueBytes = new List<byte>();
            foreach (object obj in value)
            {
                byte[] bytes = null;
                switch (obj.GetType().Name)
                {
                    case "Int32":
                    case "UInt32":
                    case "Single":
                        bytes = new byte[4];
                        break;
                    case "Int64":
                    case "UInt64":
                    case "Double":
                        bytes = new byte[8];
                        break;
                    case "Int16":
                    case "UInt16":
                        bytes = new byte[2];
                        break;
                    case "Boolean":
                    case "Byte":
                    case "String":
                        throw new Exception($"没有实现 {obj.GetType().Name}");
                }
                StructToBytes.GetBytes(obj, bytes, 0, _endianType);
                valueBytes.AddRange(bytes);
            }
            if (valueBytes.Count > 256)
            {
                throw new Exception("超过modbus最大写入字节");
            }
            list.Add(0);
            list.Add((byte)(valueBytes.Count / 2));
            list.Add((byte)valueBytes.Count);
            list.AddRange(valueBytes);
            var response = SendToRead(list.ToArray(), this);
            return response != null && response.Length >= 8;
        }

        public bool Write(int[] address, List<object> value)
        {
            throw new NullReferenceException("CIP协议用");
        }

        public bool WriteSingle(int address, object value, int length)
        {
            Type type = value.GetType();
            if (type.IsArray)
            {
                throw new Exception("请使用Write方法写入数组数据");
            }
            List<byte> list = new List<byte>() { 0x10 };
            list.Add((byte)(address >> 8));
            list.Add((byte)address);
            byte[] bytes = null;
            switch (type.Name)
            {
                case "Int32":
                case "UInt32":
                case "Single":
                    bytes = new byte[4];
                    list.Add(0);
                    list.Add(2);
                    list.Add(4);
                    break;
                case "Double":
                    bytes = new byte[8];
                    list.Add(0);
                    list.Add(4);
                    list.Add(8);
                    break;
                case "Int16":
                case "UInt16":
                    list.Add(0);
                    list.Add(1);
                    list.Add(2);
                    bytes = new byte[2];
                    break;
                case "Boolean":
                case "Byte":
                case "Int64":
                case "UInt64":
                    throw new Exception($"没有实现 {type.Name}");
                case "String":
                    List<byte> list2 = new List<byte>(Encoding.ASCII.GetBytes((string)value));
                    if (list2.Count % 2 != 0)
                    {
                        list2.Add(0);
                    }
                    list.Add(0);
                    list.Add((byte)(list2.Count / 2));
                    list.Add((byte)list2.Count);
                    bytes = list2.ToArray();
                    break;
            }
            if (type.Name != "String")
            {
                StructToBytes.GetBytes(value, bytes, 0, _endianType);
            }
            list.AddRange(bytes);
            bytes = SendToRead(list.ToArray(), this);
            return bytes != null && bytes.Length >= 8;
        }

        public IEnumerable<object> Read(int[] address, int length = 3)
        {
            throw new NullReferenceException("此协议不可用");
        }
    }
}
